package com.cqie.tes.config;

import com.cqie.tes.comm.R;
import com.cqie.tes.config.jwt.AjaxAuthenticationEntryPoint;
import com.cqie.tes.config.jwt.JwtAuthenticationTokenFilter;
import com.cqie.tes.config.jwt.MyAuthenticationSuccessHandler;
import com.cqie.tes.config.jwt.RestAuthenticationEntryPoint;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    AjaxAuthenticationEntryPoint authenticationEntryPoint;
    @Autowired
    MyAuthenticationSuccessHandler authenticationSuccessHandler;
    @Autowired
    private MyUserService userDetailsService;
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    @Autowired
    private VerifyCodeFilter verifyCodeFilter;
    @Autowired
    MyUserService myUserService;

    @Override
    public void configure(WebSecurity web) throws Exception {
//        super.configure(web);
        web.ignoring()
                .antMatchers(HttpMethod.GET,
                        "/swagger-resources/**",
                        "/PearAdmin/**",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/swagger-ui.html",
                        "/webjars/**",
                        "/v2/**");//放行静态资源
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http.authorizeRequests()
//        .antMatchers(HttpMethod.GET,
//                "/swagger-resources/**",
//                "/PearAdmin/**",
//                "/**/*.html",
//                "/**/*.css",
//                "/**/*.js",
//                "/swagger-ui.html",
//                "/webjars/**",
//                "/v2/**",
//                "/",
//                "/doc.html",
//                "/login");//放行静态资源


        http.csrf().disable()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)

            .and()
            .authorizeRequests()
            .antMatchers("/", "/doc.html", "/login").permitAll() //设置放行路径
//            .anyRequest().authenticated()   // 其他地址的访问均需验证权限

            .and()
            .formLogin()
            .loginProcessingUrl("/login")// 登录处理接口
//                .successHandler(authenticationSuccessHandler) // 登录成功处理器
            .permitAll()

//                .and()
//                .httpBasic()
//                .authenticationEntryPoint(restAuthenticationEntryPoint)//未登陆时返回 JSON 格式的数据给前端，否则是html

            .and()
            .exceptionHandling()
            .authenticationEntryPoint(authenticationEntryPoint)//匿名用户访问无权限资源时的异常处理;
            //关闭csrf
            .and()
            .headers()
            .frameOptions()
            .disable()
        ;

        http.cors(); // 允许跨域

        http.headers().cacheControl();// 禁用缓存
        // TODO 验证码效验不能成功，因为答案在session里
//        http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class); //验证码
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);// 添加JWT拦截器

        http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new MyPasswordEncoder();
    }

    /**
     * 配置整个用户信息从哪里获得
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserService).passwordEncoder(passwordEncoder());
    }


    //    注入的用于管理登录过程的各种异常
    @Bean
    UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
        UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new UsernamePasswordAuthenticationFilter();
        usernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
        usernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException exception) throws IOException, ServletException {
                httpServletResponse.setContentType("application/json;charset=utf-8");
                PrintWriter out = httpServletResponse.getWriter();
                R respBean = R.error().message(exception.getMessage());
                if (exception instanceof LockedException) {
                    respBean.message("账户被锁定，请联系管理员!");
                } else if (exception instanceof CredentialsExpiredException) {
                    respBean.message("密码过期，请联系管理员!");
                } else if (exception instanceof AccountExpiredException) {
                    respBean.message("账户过期，请联系管理员!");
                } else if (exception instanceof DisabledException) {
                    respBean.message("账户被禁用，请联系管理员!");
                } else if (exception instanceof BadCredentialsException) {
                    respBean.message("用户名或者密码输入错误，请重新输入!");
                }
                out.write(new ObjectMapper().writeValueAsString(respBean));
                out.flush();
                out.close();
            }
        });
        usernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
        return usernamePasswordAuthenticationFilter;
    }


}
